//: -ofE:\cvslog.exe

import
	tango.sys.Environment,
	tango.time.Time,
	Txt	= tango.text.Util,
	TimS	= tango.text.convert.TimeStamp ,
	tango.core.Array,
	tango.io.device.Array,
	tango.io.device.File,
	tango.io.Stdout;

import tango.io.device.Conduit : InputFilter, InputBuffer, InputStream;

extern(C){
	int memcpy(void*, void*, int);
}

// cvs log -rEXT_MULTIUSERCONTROLPANEL_8 -N -d">20101009" -wshine-changl > ..\cvs.log

void main(){
	Environment.cwd(`E:\`);
	
	auto cl	= Tokz.file(`cvs.log`, "=============================================================================");
	
	static const me_i	= `shine-changl` ;
	static const split_i	= `----------------------------` ;
	static const file_i	= `RCS file:` ;
	static const date_i	= `date:` ;
	static const path_i	= `keepit/` ;
	Tokz cl2;
	foreach( b; *cl){
		int i	= Txt.locatePattern( b, me_i);
		if( i == b.length ){
			continue ;
		}
		i	= Txt.locatePattern( b,  split_i) ;
		if( i == b.length ){
			continue ;
		}
		char[] tmp	= Txt.trim(b[ i + split_i.length .. $]) ;
		cl2.Init(tmp, `----------------------------`);
		
		i			= Txt.locatePattern( b,  file_i) ;	
		char[] file		= b[i+ file_i.length ..$];
		i			= find(file, ',');
		file			= Txt.trim(file[0..i]);
		i			= find(file, path_i);
		file			= file[ i + path_i.length .. $ ];
		
		foreach( log; cl2){
			i	= Txt.locatePattern( log,  date_i) ;
			char[] date	= log[i  + date_i.length .. $];
			i	= find(date, ';');
			char[] msg	= date[i..$];
			date	= Txt.trim( date[0..i] );
			i	= find(msg, '\n');
			msg	= Txt.trim(msg[i..$]);
			Log.add(msg, date, file);
		}
	}
	Log.sort;
	
	Array bu	= new Array(1024 * 16, 1024 * 16);
	foreach( log; Log.list){
		bu("Date: ")(log.date)("\nComment: ")(log.msg1)("\nFiles:\n");
		foreach(_file; log.files){
			bu("	")(_file)("\n");
		}
		bu("\n");
	}
	//Stdout.formatln("{}", cast(char[]) bu.slice );
	File.set(`cvslog.txt`, bu.slice);
}

struct Log {
	Time	_time ;
	char[] date;
	char[] _msg ;
	char[][] files ;
	
	static Log*[] list ;
	
	char[] msg1(){
		int i	= find(_msg, '-' ) ;
		if( i < _msg.length ){
			return Txt.trim(_msg[ i + 2 .. $ ]) ;	
		}
		return _msg ;
	}
	
	static void add(char[] msg, char[] date, char[] file){
		Time time;
		int i		= find(date, '+');
		date[i-1]	= ',' ;
		date[i]	= '0' ;
		date		= date[0..$-2];
		i	= TimS.iso8601 (date, time);
		assert( i == date.length);
		date	= date[0 .. $-4] ;

		Log* ptr	= null ;
		foreach( _log; list){
			if(  _log._msg == msg ){
				auto delta	= (time - _log._time).seconds  ;
				//if( delta < 50 ){
					ptr	= _log ;
					break;
				//}
			}
		}
		if( ptr is null ){
			ptr	= new Log;
			ptr._msg	= msg.dup ;
			ptr.date	= date.dup ;
			ptr._time	= time ;
			list	~= ptr ;
		}
		bool isInclude = false ;
		foreach( _file; ptr.files ){
			if( _file == file ){
				isInclude	= true ;
				break ;
			}
		}
		if( !isInclude ){
			ptr.files	~= file.dup ;
		}
	}
	
	static void sort(){
		.sort(list, &sort_date);
	}
	
	static bool sort_date(Log* a, Log* b){
		return a._time > b._time ;
	}
}


struct Tokz{

	InputStream	input ;
	char[]		split ;
	char[]		tmp ;
	int			_from , _to = 0 ;
	int			_max	= 1024 * 1024 * 16  ;
	
	static Tokz*  file(char[] _path, char[] _split, int buf_len = 1024 ){
		if( _split is null || _split.length is 0){
			return null ;
		}
		if( _path is null || _path.length is 0){
			return null ;
		}
		Tokz* cl		= new Tokz ;
		cl.input		= new File(_path) ;
		cl.split		= _split ;
		cl.tmp.length	= buf_len  ;
		return cl ;
	}
	
	void Init(char[] data, char[] _split, int buf_len = 1024){
		if( _split is null || _split.length is 0){
			throw new Exception(`invalid split`);
		}
		input	= new Array(data) ;
		if( tmp is null || tmp.length is 0 ){
			tmp	= new char[buf_len] ;
		}
		_from	= 0;
		_to	= 0 ;
		split	= _split ;
	}
	
	int opApply (int delegate(ref char[]) dg) {
                int	result;
		int	idle,	read	,  data_len ;
		bool	isNeedRead	= true ;
		bool	isMoreData	= true ;
		do {
			if( isNeedRead ){
				if( _to >= tmp.length  ){
					if( tmp.length >= _max ){
						throw new Exception(`memory overflow`);
					}
					tmp.length	= tmp.length * 2 ;
				}
				idle	= tmp.length - _to ;
				read	= input.read(tmp[ _to .. $]) ;
				if( read > idle ){
					throw new Exception(`file read error`);
				}
				if( read < 1 ){
					isMoreData	= false ;
				}
				_to	+= read ;
				isNeedRead	= false ;
			}
			
			char[] block	= tmp[ _from .. _to ] ;
			int _pos		= Txt.locatePattern( block, split);
			
			if( _pos == block.length ){
				if( isMoreData ) {
					isNeedRead	= true ;
					continue ;
				}
				result 	= dg ( block ) ;
				break ;
			}
			
			char[] it	= block[0 .. _pos] ;
			result 	= dg ( it ) ;
			_from		+= _pos + split.length ;
			assert( _from <= _to) ;
			
			data_len	= _to - _from ;
			
			if( _from >= data_len ){
				memcpy(&tmp[0], &tmp[_from], data_len) ;
				_from	= 0 ;
				_to	= data_len ;
			}
		} while (!result);
                return result;
        }
}

